﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Noris.Tools.QCode.QrEncoder
{
    #region BitArray
    /// <summary>
    /// <p>A simple, fast array of bits, represented compactly by an array of ints internally.
    /// </p>
    /// </summary>
    internal sealed class BitArray
    {
        internal int Size
        {
            get
            {
                return size;
            }

        }

        // TODO: I have changed these members to be internal so ProGuard can inline get() and set(). Ideally
        // they'd be private and we'd use the -allowaccessmodification flag, but Dalvik rejects the
        // resulting binary at runtime on Android. If we find a solution to this, these should be changed
        // back to private.
        internal int[] bits;
        internal int size;
        internal BitArray(int size)
        {
            if (size < 1)
            {
                throw new System.ArgumentException("size must be at least 1");
            }
            this.size = size;
            this.bits = makeArray(size);
        }

        /// <param name="i">bit to get
        /// </param>
        /// <returns> true iff bit i is set
        /// </returns>
        internal bool get_Renamed(int i)
        {
            return (bits[i >> 5] & (1 << (i & 0x1F))) != 0;
        }

        /// <summary> Sets bit i.
        /// 
        /// </summary>
        /// <param name="i">bit to set
        /// </param>
        internal void set_Renamed(int i)
        {
            bits[i >> 5] |= 1 << (i & 0x1F);
        }

        /// <summary> Flips bit i.
        /// 
        /// </summary>
        /// <param name="i">bit to set
        /// </param>
        internal void flip(int i)
        {
            bits[i >> 5] ^= 1 << (i & 0x1F);
        }

        /// <summary> Sets a block of 32 bits, starting at bit i.
        /// 
        /// </summary>
        /// <param name="i">first bit to set
        /// </param>
        /// <param name="newBits">the new value of the next 32 bits. Note again that the least-significant bit
        /// corresponds to bit i, the next-least-significant to i+1, and so on.
        /// </param>
        internal void setBulk(int i, int newBits)
        {
            bits[i >> 5] = newBits;
        }

        /// <summary> Clears all bits (sets to false).</summary>
        internal void clear()
        {
            int max = bits.Length;
            for (int i = 0; i < max; i++)
            {
                bits[i] = 0;
            }
        }

        /// <summary> Efficient method to check if a range of bits is set, or not set.
        /// 
        /// </summary>
        /// <param name="start">start of range, inclusive.
        /// </param>
        /// <param name="end">end of range, exclusive
        /// </param>
        /// <param name="value">if true, checks that bits in range are set, otherwise checks that they are not set
        /// </param>
        /// <returns> true iff all bits are set or not set in range, according to value argument
        /// </returns>
        /// <throws>  IllegalArgumentException if end is less than or equal to start </throws>
        internal bool isRange(int start, int end, bool value_Renamed)
        {
            if (end < start)
            {
                throw new System.ArgumentException();
            }
            if (end == start)
            {
                return true; // empty range matches
            }
            end--; // will be easier to treat this as the last actually set bit -- inclusive    
            int firstInt = start >> 5;
            int lastInt = end >> 5;
            for (int i = firstInt; i <= lastInt; i++)
            {
                int firstBit = i > firstInt ? 0 : start & 0x1F;
                int lastBit = i < lastInt ? 31 : end & 0x1F;
                int mask;
                if (firstBit == 0 && lastBit == 31)
                {
                    mask = -1;
                }
                else
                {
                    mask = 0;
                    for (int j = firstBit; j <= lastBit; j++)
                    {
                        mask |= 1 << j;
                    }
                }

                // Return false if we're looking for 1s and the masked bits[i] isn't all 1s (that is,
                // equals the mask, or we're looking for 0s and the masked portion is not all 0s
                if ((bits[i] & mask) != (value_Renamed ? mask : 0))
                {
                    return false;
                }
            }
            return true;
        }

        /// <returns> underlying array of ints. The first element holds the first 32 bits, and the least
        /// significant bit is bit 0.
        /// </returns>
        internal int[] getBitArray()
        {
            return bits;
        }

        /// <summary> Reverses all bits in the array.</summary>
        internal void reverse()
        {
            int[] newBits = new int[bits.Length];
            int size = this.size;
            for (int i = 0; i < size; i++)
            {
                if (get_Renamed(size - i - 1))
                {
                    newBits[i >> 5] |= 1 << (i & 0x1F);
                }
            }
            bits = newBits;
        }

        private static int[] makeArray(int size)
        {
            int arraySize = size >> 5;
            if ((size & 0x1F) != 0)
            {
                arraySize++;
            }
            return new int[arraySize];
        }

        public override System.String ToString()
        {
            System.Text.StringBuilder result = new System.Text.StringBuilder(size);
            for (int i = 0; i < size; i++)
            {
                if ((i & 0x07) == 0)
                {
                    result.Append(' ');
                }
                result.Append(get_Renamed(i) ? 'X' : '.');
            }
            return result.ToString();
        }
    }
    #endregion
    #region BitMatrix
    /// <summary>
    /// <p>Represents a 2D matrix of bits. In function arguments below, and throughout the common
    /// module, x is the column position, and y is the row position. The ordering is always x, y.
    /// The origin is at the top-left.</p>
    /// 
    /// <p>Internally the bits are represented in a 1-D array of 32-bit ints. However, each row begins
    /// with a new int. This is done intentionally so that we can copy out a row into a BitArray very
    /// efficiently.</p>
    /// 
    /// <p>The ordering of bits is row-major. Within each int, the least significant bits are used first,
    /// meaning they represent lower x values. This is compatible with BitArray's implementation.</p>
    /// 
    /// </summary>
    /// <author>  Sean Owen
    /// </author>
    /// <author>  dswitkin@google.com (Daniel Switkin)
    /// </author>
    /// <author>www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source 
    /// </author>
    internal sealed class BitMatrixInternal
    {
        /// <returns> The width of the matrix
        /// </returns>
        internal int Width
        {
            get
            {
                return width;
            }

        }
        /// <returns> The height of the matrix
        /// </returns>
        internal int Height
        {
            get
            {
                return height;
            }

        }
        /// <summary> This method is for compatibility with older code. It's only logical to call if the matrix
        /// is square, so I'm throwing if that's not the case.
        /// 
        /// </summary>
        /// <returns> row/column dimension of this matrix
        /// </returns>
        internal int Dimension
        {
            get
            {
                if (width != height)
                {
                    throw new System.SystemException("Can't call getDimension() on a non-square matrix");
                }
                return width;
            }

        }

        // TODO: Just like BitArray, these need to be internal so ProGuard can inline them.
        //UPGRADE_NOTE: Final was removed from the declaration of 'width '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
        internal int width;
        //UPGRADE_NOTE: Final was removed from the declaration of 'height '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
        internal int height;
        //UPGRADE_NOTE: Final was removed from the declaration of 'rowSize '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
        internal int rowSize;
        //UPGRADE_NOTE: Final was removed from the declaration of 'bits '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
        internal int[] bits;

        // A helper to construct a square matrix.
        internal BitMatrixInternal(int dimension)
            : this(dimension, dimension)
        {
        }

        internal BitMatrixInternal(int width, int height)
        {
            if (width < 1 || height < 1)
            {
                throw new System.ArgumentException("Both dimensions must be greater than 0");
            }
            this.width = width;
            this.height = height;
            int rowSize = width >> 5;
            if ((width & 0x1f) != 0)
            {
                rowSize++;
            }
            this.rowSize = rowSize;
            bits = new int[rowSize * height];
        }

        /// <summary> <p>Gets the requested bit, where true means black.</p>
        /// 
        /// </summary>
        /// <param name="x">The horizontal component (i.e. which column)
        /// </param>
        /// <param name="y">The vertical component (i.e. which row)
        /// </param>
        /// <returns> value of given bit in matrix
        /// </returns>
        internal bool get_Renamed(int x, int y)
        {
            int offset = y * rowSize + (x >> 5);
            return ((SupportClass.URShift(bits[offset], (x & 0x1f))) & 1) != 0;
        }

        /// <summary> <p>Sets the given bit to true.</p>
        /// 
        /// </summary>
        /// <param name="x">The horizontal component (i.e. which column)
        /// </param>
        /// <param name="y">The vertical component (i.e. which row)
        /// </param>
        internal void set_Renamed(int x, int y)
        {
            int offset = y * rowSize + (x >> 5);
            bits[offset] |= 1 << (x & 0x1f);
        }

        /// <summary> <p>Flips the given bit.</p>
        /// 
        /// </summary>
        /// <param name="x">The horizontal component (i.e. which column)
        /// </param>
        /// <param name="y">The vertical component (i.e. which row)
        /// </param>
        internal void flip(int x, int y)
        {
            int offset = y * rowSize + (x >> 5);
            bits[offset] ^= 1 << (x & 0x1f);
        }

        /// <summary> Clears all bits (sets to false).</summary>
        internal void clear()
        {
            int max = bits.Length;
            for (int i = 0; i < max; i++)
            {
                bits[i] = 0;
            }
        }

        /// <summary> <p>Sets a square region of the bit matrix to true.</p>
        /// 
        /// </summary>
        /// <param name="left">The horizontal position to begin at (inclusive)
        /// </param>
        /// <param name="top">The vertical position to begin at (inclusive)
        /// </param>
        /// <param name="width">The width of the region
        /// </param>
        /// <param name="height">The height of the region
        /// </param>
        internal void setRegion(int left, int top, int width, int height)
        {
            if (top < 0 || left < 0)
            {
                throw new System.ArgumentException("Left and top must be nonnegative");
            }
            if (height < 1 || width < 1)
            {
                throw new System.ArgumentException("Height and width must be at least 1");
            }
            int right = left + width;
            int bottom = top + height;
            if (bottom > this.height || right > this.width)
            {
                throw new System.ArgumentException("The region must fit inside the matrix");
            }
            for (int y = top; y < bottom; y++)
            {
                int offset = y * rowSize;
                for (int x = left; x < right; x++)
                {
                    bits[offset + (x >> 5)] |= 1 << (x & 0x1f);
                }
            }
        }

        /// <summary> A fast method to retrieve one row of data from the matrix as a BitArray.
        /// 
        /// </summary>
        /// <param name="y">The row to retrieve
        /// </param>
        /// <param name="row">An optional caller-allocated BitArray, will be allocated if null or too small
        /// </param>
        /// <returns> The resulting BitArray - this reference should always be used even when passing
        /// your own row
        /// </returns>
        internal BitArray getRow(int y, BitArray row)
        {
            if (row == null || row.Size < width)
            {
                row = new BitArray(width);
            }
            int offset = y * rowSize;
            for (int x = 0; x < rowSize; x++)
            {
                row.setBulk(x << 5, bits[offset + x]);
            }
            return row;
        }

        public override System.String ToString()
        {
            System.Text.StringBuilder result = new System.Text.StringBuilder(height * (width + 1));
            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    result.Append(get_Renamed(x, y) ? "X " : "  ");
                }
                result.Append('\n');
            }
            return result.ToString();
        }
    }
    #endregion
    #region ByteArray
    /// <summary>
    /// This class implements an array of unsigned bytes.
    /// </summary>
    internal sealed class ByteArray
    {
        internal bool Empty
        {
            get
            {
                return size_Renamed_Field == 0;
            }

        }

        private const int INITIAL_SIZE = 32;

        private sbyte[] bytes;
        private int size_Renamed_Field;

        internal ByteArray()
        {
            bytes = null;
            size_Renamed_Field = 0;
        }

        internal ByteArray(int size)
        {
            bytes = new sbyte[size];
            this.size_Renamed_Field = size;
        }

        internal ByteArray(sbyte[] byteArray)
        {
            bytes = byteArray;
            size_Renamed_Field = bytes.Length;
        }

        /// <summary> Access an unsigned byte at location index.</summary>
        /// <param name="index">The index in the array to access.
        /// </param>
        /// <returns> The unsigned value of the byte as an int.
        /// </returns>
        internal int at(int index)
        {
            return bytes[index] & 0xff;
        }

        internal void set_Renamed(int index, int value_Renamed)
        {
            bytes[index] = (sbyte)value_Renamed;
        }

        internal int size()
        {
            return size_Renamed_Field;
        }

        internal void appendByte(int value_Renamed)
        {
            if (size_Renamed_Field == 0 || size_Renamed_Field >= bytes.Length)
            {
                int newSize = System.Math.Max(INITIAL_SIZE, size_Renamed_Field << 1);
                reserve(newSize);
            }
            bytes[size_Renamed_Field] = (sbyte)value_Renamed;
            size_Renamed_Field++;
        }

        internal void reserve(int capacity)
        {
            if (bytes == null || bytes.Length < capacity)
            {
                sbyte[] newArray = new sbyte[capacity];
                if (bytes != null)
                {
                    Array.Copy(bytes, 0, newArray, 0, bytes.Length);
                }
                bytes = newArray;
            }
        }

        // Copy count bytes from array source starting at offset.
        internal void set_Renamed(sbyte[] source, int offset, int count)
        {
            bytes = new sbyte[count];
            size_Renamed_Field = count;
            for (int x = 0; x < count; x++)
            {
                bytes[x] = source[offset + x];
            }
        }
    }
    #endregion
    #region ByteMatrix
    /// <summary>
    /// A class which wraps a 2D array of bytes. The default usage is signed. If you want to use it as a
    /// unsigned container, it's up to you to do byteValue (and) 0xff at each location.
    /// JAVAPORT: The original code was a 2D array of ints, but since it only ever gets assigned
    /// -1, 0, and 1, I'm going to use less memory and go with bytes.
    /// </summary>
    internal sealed class ByteMatrix
    {
        internal ByteMatrix(int width, int height)
        {
            this._Bytes = new sbyte[height][];
            for (int i = 0; i < height; i++)
                this._Bytes[i] = new sbyte[width];
            
            this._Width = width;
            this._Height = height;
        }
        internal int Height { get { return _Height; } }
        internal int Width { get { return _Width; } }
        internal sbyte[][] Array { get { return _Bytes; } }
        private sbyte[][] _Bytes;
        private int _Width;
        private int _Height;

        internal sbyte get_Renamed(int x, int y)
        {
            return _Bytes[y][x];
        }

        internal void set_Renamed(int x, int y, sbyte value_Renamed)
        {
            _Bytes[y][x] = value_Renamed;
        }

        internal void set_Renamed(int x, int y, int value_Renamed)
        {
            _Bytes[y][x] = (sbyte)value_Renamed;
        }

        internal void clear(sbyte value_Renamed)
        {
            for (int y = 0; y < _Height; ++y)
            {
                for (int x = 0; x < _Width; ++x)
                {
                    _Bytes[y][x] = value_Renamed;
                }
            }
        }

        public override System.String ToString()
        {
            System.Text.StringBuilder result = new System.Text.StringBuilder(2 * _Width * _Height + 2);
            for (int y = 0; y < _Height; ++y)
            {
                for (int x = 0; x < _Width; ++x)
                {
                    switch (_Bytes[y][x])
                    {

                        case 0:
                            result.Append("0 ");
                            break;

                        case 1:
                            result.Append("1 ");
                            break;

                        default:
                            result.Append("  ");
                            break;

                    }
                }
                result.Append('\n');
            }
            return result.ToString();
        }
    }
    #endregion
}
